package cn.iocoder.yudao.module.pay.framework.impl.mallbook;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.module.api.pay.order.dto.UnifiedOrderCreateDTO;
import cn.iocoder.yudao.module.api.pay.order.eums.UnifiedPayChannelEnum;
import cn.iocoder.yudao.module.api.pay.order.eums.UnifiedPayDisplayModeEnum;
import cn.iocoder.yudao.module.api.pay.order.vo.UnifiedOrderVO;
import cn.iocoder.yudao.module.api.pay.refund.dto.UnifiedRefundCreateDTO;
import cn.iocoder.yudao.module.api.pay.refund.vo.UnifiedRefundVO;
import cn.iocoder.yudao.module.api.pay.transfer.dto.UnifiedTransferCreateDTO;
import cn.iocoder.yudao.module.api.pay.transfer.enums.UnifiedTransferPeriodType;
import cn.iocoder.yudao.module.api.pay.transfer.vo.UnifiedTransferVO;
import cn.iocoder.yudao.module.pay.framework.impl.AbstractPayClient;
import cn.iocoder.yudao.module.pay.framework.impl.mallbook.domain.PaymentNotifyResponse;
import com.trhui.mallbook.client.PaymentClient;
import com.trhui.mallbook.client.RefundClient;
import com.trhui.mallbook.client.TradeQueryClient;
import com.trhui.mallbook.client.WithdrawClient;
import com.trhui.mallbook.config.MallBookConfig;
import com.trhui.mallbook.domain.common.ApiResponse;
import com.trhui.mallbook.domain.common.BaseResponse;
import com.trhui.mallbook.domain.request.Goods;
import com.trhui.mallbook.domain.request.QueryOrderRequest;
import com.trhui.mallbook.domain.request.hf.HfPayOrderCloseRequest;
import com.trhui.mallbook.domain.request.hf.HfPaymentOrderRequest;
import com.trhui.mallbook.domain.request.hf.HfRefundRequest;
import com.trhui.mallbook.domain.request.hf.HfWithdrawRequest;
import com.trhui.mallbook.domain.response.hf.HfPayOrderCloseResponse;
import com.trhui.mallbook.domain.response.hf.HfPaymentOrderResponse;
import com.trhui.mallbook.domain.response.hf.HfRefundResponse;
import com.trhui.mallbook.domain.response.hf.HfWithdrawResponse;
import com.trhui.mallbook.domain.response.trade.PaymentQueryResponse;
import com.trhui.mallbook.domain.response.trade.RefundQueryResponse;
import com.trhui.mallbook.domain.response.trade.WithdrawQueryResponse;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * MallBook 聚合支付的 PayClient 实现类
 *
 * @author 山野羡民
 * @since 2025/05/16
 */
@Slf4j
public class MallBookWxLitePayClient extends AbstractPayClient<MallBookPayClientConfig> {

    public MallBookWxLitePayClient(MallBookPayClientConfig config) {
        super(UnifiedPayChannelEnum.MALLBOOK_WX_LITE.getCode(), config);
    }

    @Override
    protected void doInit() {
        MallBookConfig mallBookConfig = new MallBookConfig();
        mallBookConfig.setMerchantPrivateKey(channelConfig.getMerchantPrivateKey());
        mallBookConfig.setDebug(true);
        mallBookConfig.setProdEnvironment(true);
        mallBookConfig.setApiBase(channelConfig.getBaseUrl());
        mallBookConfig.setMerchantNo(channelConfig.getMerchantNo());
        mallBookConfig.setFileUploadDebug(true);
        mallBookConfig.setTimeout(6000);
        mallBookConfig.init();
    }

    @Override
    protected UnifiedOrderVO doUnifiedOrder(UnifiedOrderCreateDTO dto) {
        //- 支付场景说明：
        //  - 直接支付同步分账：上传参数（transferType：1, asynSplitFlag：0），支付时上传对应收款方子商户编号(payeeUserId)和分账方列表（splitList），支付完成后资金直接同步分账到各子商户可结算资金。
        //  - 担保交易同步分账：上传参数（transferType：0 ,asynSplitFlag：0），支付时上传对应收款方子商户编号(payeeUserId)和分账方列表（splitList），支付完成后资金直接同步分账到各子商户在途资金，需要调起确认收货接口各子商户在途资金将转为可结算账户。
        //  - 担保交易单次异步分账：上传参数（transferType：0 ,asynSplitFlag：1），支付时上传对应收款方子商户编号(payeeUserId)和分账方列表（splitList），支付完成后资金预先进入各子商户在途资金。需要调起异步分账接口重新确认分账方和分账金额，但时必须包含收款方且分账金额不能为0，异步分账完成后原先子商户在途金额将调账到其他子商户在途资金账户上，此时还需调起确认收货接口，重新分配的子商户在途资金转入可结算账户。（退款前需要进行异步分账）
        //  - 担保交易多次异步分账：上传参数（transferType：0 ,asynSplitFlag：1），支付时不需上传对应收款方子商户编号(payeeUserId)和分账方列表（splitList），支付完成后资金预先进入平台运营资金。调起异步分账接口给需要分账方进行分账，运营资金将转入对应分账方的在途资金。每次异步分账需要对应确认收货，在途资金将转入可结算账户。
        // 接口文档，参阅：https://o2yyifwsjv.feishu.cn/docx/WaV7diAKMoNwt2xsfRVcXwxZn3d#doxcnw5ahXonugcBF5pxa6UhZDc
        HfPaymentOrderRequest request = new HfPaymentOrderRequest();
        /*
         * 平台商户号
         */
        request.setMerchantNo(channelConfig.getMerchantNo());
        /*
         * 请求订单号
         */
        request.setMerOrderId(dto.getPayNo());
        /*
         * 交易类型（必填）字段长度最长：
         * 0：担保交易，需要调用确认收货接口，资金才会划转到对方账户；
         * 1：直接支付，支付成功后，资金直接划转到对方账户中。
         */
        request.setTransferType("0");
        /*
         * 是否异步分账（必填）字段长度最长： 0：否 1：是
         * 当交易类型为直接支付时，不支持异步分账。
         */
        request.setAsynSplitFlag("1");
        /*
         * 业务订单号（必填）字段长度最长：64位
         */
        request.setBizOrderId(dto.getTradeNo());
        /*
         * 交易金额（必填）字段长度最长：12位
         * 单位分，必须大于0的整数 ，如1.12元，传的时候乘以100，值为：112；
         * 交易金额=总分账金额
         */
        request.setAmount(String.valueOf(dto.getPrice()));
        /*
         * 订单信息（必填）字段长度最长：100位
         */
        request.setOrderName(dto.getSubject());
        /*
         * 支付类型（必填）字段长度最长：20位
         * WX_JSAPI：微信公众号
         * WX_MINI：微信小程序
         * ALI_CB：支付宝C扫B
         * ALI_JSAPI：支付宝生活号
         * ALI_MINI：支付宝小程序
         * ALI_APP：支付宝APP
         * ALI_H5：支付宝H5支付
         * SCAN：付款码支付
         * B2C：个人网银支付
         * B2B：企业 网银支付
         * FAST_PAY：快捷支付
         * BALANCE：余额支付
         */
        request.setPayType("WX_MINI");
        /*
         * 终端IP（必填）字段长度最长：20位
         */
        request.setTerminalIp(dto.getUserIp());
        /*
         * 商品列表（必填）
         */
        List<Goods> goodsDetail = new ArrayList<>();
        Goods good = new Goods();
        good.setGoodsName(dto.getBody());
        good.setQuantity("1");
        good.setPrice(String.valueOf(dto.getPrice()));
        goodsDetail.add(good);
        request.setGoodsDetail(goodsDetail);
        /*
         *后台回调地址
         */
        request.setNotifyUrl(dto.getNotifyUrl());
        /*
         *前台回调地址
         */
        request.setFrontUrl(StrUtil.isBlank(dto.getRedirectUrl()) ? dto.getNotifyUrl() : dto.getRedirectUrl());
        /*
         * 微信或支付宝的appid
         * 支付类型为微信小程序时必填
         */
        String appId = MapUtil.getStr(dto.getChannelExtras(), "app_id", "");
        request.setAppid(appId);
        /*
         * 微信或支付宝用户id
         * 支付类型为支付宝小程序/支付宝生活号/微信公众号/微信小程序必填
         */
        String openid = MapUtil.getStr(dto.getChannelExtras(), "openid", "");
        request.setOpenid(openid);
        BaseResponse<HfPaymentOrderResponse> response = PaymentClient.hfPayment(request);
        //{"code":"1001","msg":"数据验证失败： frontUrl: 前台回调地址不能为空","date":"1747397747190","version":"1.3.0"}
        //{"code":"1346","msg":"请求渠道出错，请联系MallBook客服。【errorMsg：账户可用余额不足】","date":"1747398020042","version":"1.3.0"}
        //{"code":"1311","msg":"总分账金额必须等于交易金额","date":"1747460763089","version":"1.3.0"}
        log.info("[doUnifiedOrder] PaymentClient.hfPayment: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        HfPaymentOrderResponse data = response.getData();
        return UnifiedOrderVO.waitingOf(UnifiedPayDisplayModeEnum.APP.getCode(), data.getPayCode(), dto.getPayNo(), data.getChannelOrderId(), response);
    }

    @Override
    protected UnifiedOrderVO doGetOrder(String payNo) {
        QueryOrderRequest request = new QueryOrderRequest();
        /*
         * 平台商户号
         */
        request.setMerchantNo(channelConfig.getMerchantNo());
        /*
         * 请求订单号
         */
        request.setMerOrderId(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        /*
         * 原请求订单号(必填)  字段长度最长：32位
         */
        request.setOriginalMerOrderId(payNo);
        /*
         * 1：支付查询 2：退款查询 3：确认收货查询 4：结算查询 5：充值查询 6：转账查询 7：异步分账查询(必填)  字段长度最长：1位
         */
        request.setQueryType("1");
        @SuppressWarnings("unchecked")
        BaseResponse<PaymentQueryResponse> response = TradeQueryClient.hfTradeQuery(request);
        //{"code":"1339","msg":"查无支付订单信息","date":"1747460536946","version":"1.3.0"}
        log.info("[doGetOrder] TradeQueryClient.hfTradeQuery: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        PaymentQueryResponse data = response.getData();
        // 交易状态：0-待处理、1-成功、2-失败、3-已取消(关单成功后状态会更新为已取消)
        if ("1".equals(data.getStatus())) {
            return UnifiedOrderVO.successOf(data.getChannelOrderId(), "", LocalDateTime.now(), payNo, response);
        }
        return UnifiedOrderVO.waitingOf(UnifiedPayDisplayModeEnum.APP.getCode(), data.getPayCode(), payNo, data.getChannelOrderId(), response);
    }

    @Override
    protected UnifiedOrderVO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) {
        ApiResponse response = MallBookPayClientConfig.Holder.INSTANCE.validateApiResponse(params);
        // 验签通过，处理业务
        // {
        //    "amount": "990",
        //    "channelOrderId": "ZF1943877919714582529",
        //    "merOrderId": "UPY202507121139468918",
        //    "openid": "ojqDx62zefb6fseUu9yJzxCyujdk",
        //    "outTransId": "4200002763202507123985212265",
        //    "partyOrderId": "022125071241986354136371",
        //    "payFinishTime": "2025-07-12 11:39:52",
        //    "splitList": [],
        //    "status": "1"
        //}
        //PaymentQueryResponse data = JSONUtil.parseObj(response.getResult()).toBean(PaymentQueryResponse.class);
        PaymentNotifyResponse data = JSONUtil.parseObj(response.getResult()).toBean(PaymentNotifyResponse.class);
        // 交易状态：0-待处理、1-成功、2-失败、3-已取消(关单成功后状态会更新为已取消)
        if ("1".equals(data.getStatus())) {
            LocalDateTime payFinishTime = LocalDateTime.now();
            if (StrUtil.isNotBlank(data.getPayFinishTime())) {
                payFinishTime = LocalDateTime.parse(data.getPayFinishTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }
            return UnifiedOrderVO.successOf(data.getChannelOrderId(), data.getOpenid(), payFinishTime, data.getMerOrderId(), response);
        }
        return UnifiedOrderVO.waitingOf(data.getMerOrderId(), data.getChannelOrderId(), response);
    }


    @Override
    protected boolean doCloseOrder(String payNo) {
        // 接口文档，参阅：https://o2yyifwsjv.feishu.cn/docx/WaV7diAKMoNwt2xsfRVcXwxZn3d#doxcnBIOZJ6svICNmRfVqsTxHV4
        HfPayOrderCloseRequest request = new HfPayOrderCloseRequest();
        request.setMerchantNo(channelConfig.getMerchantNo());
        request.setMerOrderId(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        /*
         * 原支付请求订单号
         */
        request.setOriginalMerOrderId(payNo);
        BaseResponse<HfPayOrderCloseResponse> response = PaymentClient.hfPayOrderClose(request);
        log.info("[doCloseOrder] PaymentClient.hfPayOrderClose: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        HfPayOrderCloseResponse data = response.getData();
        // 关单状态：0-待处理、1-成功、2-失败
        return "1".equals(data.getStatus());
    }

    @Override
    protected UnifiedRefundVO doUnifiedRefund(UnifiedRefundCreateDTO dto) {
        // 接口文档，参阅：https://o2yyifwsjv.feishu.cn/docx/WaV7diAKMoNwt2xsfRVcXwxZn3d#doxcntonahpUzyh8RLTFgF5NSCf
        HfRefundRequest request = new HfRefundRequest();
        request.setMerchantNo(channelConfig.getMerchantNo());
        request.setMerOrderId(dto.getRefundNo());
        // 原支付请求订单号(必填) 字段长度最长：32 位
        request.setOriginalMerOrderId(dto.getPayNo());
        // 退款总金额(必填)  全部退款：退款金额 = 支付金额
        request.setRefundAmount(String.valueOf(dto.getRefundPrice()));
        // 后台回调地址(必填) 同步响应报文中code=0000的时候，订单状态处理中，成功，失败会异步通知
        request.setNotifyUrl(dto.getNotifyUrl());
        //分账方列表 支付订单如果传了多个分账方列表，退款也需要全部传
        //request.setSplitList(splitList);
        // {"originalMerOrderId":"UPY202507041101567367","notifyUrl":"https://api.mdycjt.com/app-api/pay/refund/notify/wx_lite","parameter1":"RFD202507041149086675","refundAmount":"1"}
        BaseResponse<HfRefundResponse> response = RefundClient.hfRefund(request);
        log.info("[doUnifiedRefund] RefundClient.hfRefund: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        // {"merOrderId":"URF202507041224342032","parameter1":"PRD202507041149086675","refundAmount":"1","status":"0","statusMsg":""}
        HfRefundResponse data = response.getData();
        // 退款状态：0-待处理、1-成功、2-失败
        if ("1".equals(data.getStatus())) {
            return UnifiedRefundVO.successOf("MB" + data.getMerOrderId(), LocalDateTime.now(), dto.getRefundNo(), response);
        }
        return UnifiedRefundVO.waitingOf("MB" + data.getMerOrderId(), dto.getRefundNo(), response);
    }

    @Override
    protected UnifiedRefundVO doGetRefund(String payNo, String refundNo) {
        QueryOrderRequest request = new QueryOrderRequest();
        request.setMerchantNo(channelConfig.getMerchantNo());
        request.setMerOrderId(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        /*
         * 原请求订单号(必填)  字段长度最长：32位
         */
        request.setOriginalMerOrderId(refundNo);
        /*
         * 1：支付查询 2：退款查询 3：确认收货查询 4：结算查询 5：充值查询 6：转账查询 7：异步分账查询(必填)  字段长度最长：1位
         */
        request.setQueryType("2");
        @SuppressWarnings("unchecked")
        BaseResponse<RefundQueryResponse> response = TradeQueryClient.hfTradeQuery(request);
        log.info("[doGetRefund] TradeQueryClient.hfTradeQuery: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        RefundQueryResponse data = response.getData();
        // 退款状态：0-待处理、1-成功、2-失败
        if ("1".equals(data.getStatus())) {
            return UnifiedRefundVO.successOf("MB" + data.getMerOrderId(), LocalDateTime.now(), refundNo, response);
        }
        return UnifiedRefundVO.waitingOf("MB" + data.getMerOrderId(), refundNo, response);
    }

    @Override
    protected UnifiedRefundVO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) {
        ApiResponse response = MallBookPayClientConfig.Holder.INSTANCE.validateApiResponse(params);
        // 验签通过，处理业务
        RefundQueryResponse data = JSONUtil.parseObj(response.getResult()).toBean(RefundQueryResponse.class);
        // 退款状态：0-待处理、1-成功、2-失败
        if ("1".equals(data.getStatus())) {
            return UnifiedRefundVO.successOf("", LocalDateTime.now(), data.getMerOrderId(), response);
        }
        return UnifiedRefundVO.waitingOf("", data.getMerOrderId(), response);
    }

    @Override
    protected UnifiedTransferVO doUnifiedTransfer(UnifiedTransferCreateDTO dto) {
        // 接口文档，参阅：https://o2yyifwsjv.feishu.cn/docx/WaV7diAKMoNwt2xsfRVcXwxZn3d#doxcnoSG8KtPYWRLd1j5PbUPwRh
        HfWithdrawRequest request = new HfWithdrawRequest();
        request.setMerchantNo(channelConfig.getMerchantNo());
        request.setMerOrderId(dto.getTransferNo());
        request.setUserId(dto.getUserAccount());
        request.setAmount(String.valueOf(dto.getPrice()));
        request.setOrderName("提现");
        //request.setSettlementToAccount(UnifiedTransferPeriodType.T1.getCode());
        request.setSettlementToAccount(UnifiedTransferPeriodType.DM.getCode());
        request.setRemark(dto.getSubject());
        request.setNotifyUrl(dto.getNotifyUrl());
        BaseResponse<HfWithdrawResponse> response = WithdrawClient.hfWithdraw(request);
        log.info("[doUnifiedTransfer] WithdrawClient.hfWithdraw: code={}, msg={}", response.getCode(), response.getMsg());
        Assertions.isTrue(response.success(), response.getMsg());
        HfWithdrawResponse data = response.getData();
        // 结算状态：0-待处理、1-成功、2-失败
        if ("1".equals(data.getStatus())) {
            return UnifiedTransferVO.successOf("MB" + data.getMerOrderId(), LocalDateTime.now(), dto.getTransferNo(), response);
        }
        return UnifiedTransferVO.waitingOf(dto.getTransferNo(), response);
    }

    @Override
    protected UnifiedTransferVO doGetTransfer(String transferNo) {
        log.debug("[doGetTransfer][支付客户端({}) transferNo({})]", getChannelCode(), transferNo);
        QueryOrderRequest request = new QueryOrderRequest();
        request.setMerOrderId(MallBookPayClientConfig.Holder.INSTANCE.generateRequestNo());
        request.setMerchantNo(channelConfig.getMerchantNo());
        /*
         * 原请求订单号(必填)  字段长度最长：32位
         */
        request.setOriginalMerOrderId(transferNo);
        /*
         * 1：支付查询 2：退款查询 3：确认收货查询 4：结算查询 5：充值查询 6：转账查询 7：异步分账查询(必填)  字段长度最长：1位
         */
        request.setQueryType("4");
        @SuppressWarnings("unchecked")
        BaseResponse<WithdrawQueryResponse> response = TradeQueryClient.hfTradeQuery(request);
        log.info("[doGetWithdraw] TradeQueryClient.hfQuery: code={}, msg={}", response.getCode(), response.getMsg());
        if (!response.success()) {
            return UnifiedTransferVO.closedOf(response.getCode(), response.getMsg(), transferNo, response);
        }
        // 结算状态：0-待处理、1-成功、2-失败
        if ("1".equals(response.getData().getStatus())) {
            return UnifiedTransferVO.successOf("", LocalDateTime.now(), transferNo, response);
        }
        if ("2".equals(response.getData().getStatus())) {
            return UnifiedTransferVO.closedOf(response.getCode(), response.getMsg(), transferNo, response);
        }
        return UnifiedTransferVO.waitingOf(transferNo, response);
    }

    @Override
    protected UnifiedTransferVO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
        ApiResponse response = MallBookPayClientConfig.Holder.INSTANCE.validateApiResponse(params);
        // 验签通过，处理业务
        WithdrawQueryResponse data = JSONUtil.parseObj(response.getResult()).toBean(WithdrawQueryResponse.class);
        // 结算状态：0-待处理、1-成功、2-失败
        if ("1".equals(data.getStatus())) {
            return UnifiedTransferVO.successOf("", LocalDateTime.now(), data.getMerOrderId(), response);
        }
        return UnifiedTransferVO.waitingOf(data.getMerOrderId(), response);
    }

}
